/**
 * @file SimpleCDCTL.cpp
 * @brief CDCTL01A芯片驱动实现
 * @details 为Arduino平台提供CDCTL01A芯片的完整驱动支持
 *          支持硬件加速的CDBUS通信，包括SPI接口、寄存器配置、
 *          数据帧管理、中断处理等功能
 * 
 * @author 修改团队：mastwet@Undefine Innovation
 * @date 2024
 * @copyright MIT License
 * 
 * @note 适用于Arduino UNO、Mega、ESP32等支持SPI的开发板
 * @note CDCTL01A芯片通过SPI接口通信，支持最高8MHz SPI时钟
 * @note 支持最大64字节数据帧，自动CRC校验
 * 
 * @warning 使用前需要正确连接SPI引脚：MISO、MOSI、SCK、CS、INT
 */

#include "SimpleCDCTL.h"

/**
 * @brief 构造函数
 * @param cs_pin 片选引脚（Chip Select）
 * @param int_pin 中断引脚（Interrupt）
 * @details 创建CDCTL01A驱动对象实例
 *          初始化引脚配置和状态标志
 * 
 * @note CS引脚必须为数字输出引脚
 * @note INT引脚必须为数字输入引脚，支持中断功能
 */
SimpleCDCTL::SimpleCDCTL(uint8_t cs_pin, uint8_t int_pin) 
    : _cs_pin(cs_pin), _int_pin(int_pin), _initialized(false) {
}

/**
 * @brief 初始化CDCTL01A芯片
 * @param mac_address 设备MAC地址（0-255）
 * @param baud_rate 通信波特率（如9600、115200等）
 * @return true初始化成功，false初始化失败
 * @details 完成以下初始化步骤：
 *          1. 初始化SPI接口
 *          2. 配置引脚模式
 *          3. 初始化帧管理
 *          4. 复位芯片
 *          5. 配置寄存器参数
 *          6. 启用接收器
 *          7. 设置设备回调
 * 
 * @note 必须在调用其他函数前调用此函数
 * @note 建议使用115200或更高的波特率以获得最佳性能
 */
bool SimpleCDCTL::begin(uint8_t mac_address, uint32_t baud_rate) {
    /* 初始化SPI接口 */
    SPI.begin();
    
    /* 配置引脚模式 */
    pinMode(_cs_pin, OUTPUT);
    pinMode(_int_pin, INPUT_PULLUP);
    
    /* 初始化帧管理链表 */
    initializeFrames();
    
    /* 复位CDCTL01A芯片 */
    resetChip();
    delay(10);  /* 等待芯片稳定 */
    
    /* 配置CDCTL01A寄存器 */
    setMacAddress(mac_address);
    setBaudRate(baud_rate);
    setFilter(0xFF, 0xFF);  /* 接收所有数据帧 */
    setMode(false);  /* 仲裁模式 */
    
    /* 启用接收器 */
    writeRegister(CDREG_RX_CTRL, 0x00);
    
    /* 初始化CDBUS设备回调函数 */
    _cd_dev.get_rx_frame = getRxFrame;
    _cd_dev.put_tx_frame = putTxFrame;
    _cd_dev.priv = this;
    
    _initialized = true;
    return true;
}

/**
 * @brief 关闭CDCTL01A芯片
 * @details 禁用接收器并重置初始化状态
 *          释放相关资源
 */
void SimpleCDCTL::end() {
    if (_initialized) {
        /* 禁用接收器，清空接收缓冲区 */
        writeRegister(CDREG_RX_CTRL, CDBIT_RX_RST_ALL);
        _initialized = false;
    }
}

/**
 * @brief 设置MAC地址
 * @param mac 设备地址（0-255）
 * @details 配置CDCTL01A芯片的设备地址
 *          用于数据帧的源地址识别
 * 
 * @note 地址0为广播地址，建议使用1-254
 */
void SimpleCDCTL::setMacAddress(uint8_t mac) {
    writeRegister(CDREG_MAC, mac);
}

/**
 * @brief 设置通信波特率
 * @param low_baud 低速波特率
 * @param high_baud 高速波特率（可选，为0时使用low_baud）
 * @details 根据系统时钟计算波特率分频系数
 *          支持灵活的波特率配置
 * 
 * @note 基于48MHz系统时钟计算分频系数
 * @note 实际波特率 = 系统时钟 / 分频系数
 */
void SimpleCDCTL::setBaudRate(uint32_t low_baud, uint32_t high_baud) {
    if (high_baud == 0) {
        high_baud = low_baud;  /* 使用相同波特率 */
    }
    
    /* 计算波特率分频系数（基于48MHz系统时钟） */
    uint32_t clk_div = 48000000 / low_baud;
    uint32_t clk_div_h = 48000000 / high_baud;
    
    /* 设置波特率寄存器 */
    writeRegister(CDREG_BAUD_L, (uint8_t)(clk_div & 0xFF));
    writeRegister(CDREG_BAUD_H, (uint8_t)(clk_div_h & 0xFF));
}

/**
 * @brief 设置地址过滤器
 * @param filter0 过滤器0掩码
 * @param filter1 过滤器1掩码
 * @details 配置数据帧接收过滤器
 *          支持基于地址的精确过滤
 * 
 * @note 0xFF表示接收所有地址
 * @note 支持两个独立的过滤器
 */
void SimpleCDCTL::setFilter(uint8_t filter0, uint8_t filter1) {
    writeRegister(CDREG_FILTER_M0, filter0);
    writeRegister(CDREG_FILTER_M1, filter1);
}

/**
 * @brief 设置工作模式
 * @param break_sync true为中断同步模式，false为仲裁模式
 * @details 配置CDCTL01A的工作模式
 * 
 * @note 仲裁模式：标准CDBUS通信
 * @note 中断同步模式：用于特殊同步场景
 */
void SimpleCDCTL::setMode(bool break_sync) {
    uint8_t cfg = break_sync ? CDBIT_MODE_BREAK : CDBIT_MODE_ARB;
    writeRegister(CDREG_CFG_0, cfg);
}

/**
 * @brief 获取芯片状态
 * @return 状态寄存器值
 * @details 读取CDCTL01A的状态寄存器
 *          包含接收、发送、错误等状态信息
 */
uint8_t SimpleCDCTL::getStatus() {
    return readRegister(CDREG_STATUS);
}

/**
 * @brief 检查是否有数据可读取
 * @return true有数据，false无数据
 * @details 通过中断引脚状态判断是否有数据
 */
bool SimpleCDCTL::available() {
    return interruptPending();
}

/**
 * @brief 读取接收到的数据
 * @param buffer 数据缓冲区
 * @param max_length 最大读取长度
 * @return 实际读取的数据长度
 * @details 从接收缓冲区读取数据帧
 *          自动处理帧格式解析和数据提取
 * 
 * @note 数据格式：[源地址][目标地址][长度][数据...][CRC16]
 * @note 读取完成后自动回收帧缓冲区
 */
uint8_t SimpleCDCTL::read(uint8_t *buffer, uint8_t max_length) {
    cd_frame_t *frame = getRxFrame(&_cd_dev);
    if (!frame) {
        return 0;  /* 无数据可读取 */
    }
    
    /* 提取数据长度（位于偏移2处） */
    uint8_t length = min(frame->dat[2], max_length);
    if (length > 0) {
        memcpy(buffer, frame->dat + 3, length);  /* 数据从偏移3开始 */
    }
    
    /* 回收帧缓冲区到空闲链表 */
    cd_list_put(&_free_head, frame);
    
    return length;
}

/**
 * @brief 发送数据
 * @param data 数据缓冲区
 * @param length 数据长度
 * @return true发送成功，false发送失败
 * @details 构造完整CDBUS数据帧并发送
 *          自动添加帧头、长度、CRC校验
 * 
 * @note 最大支持64字节数据
 * @note 自动使用广播地址(0xFF)作为目标地址
 */
bool SimpleCDCTL::write(const uint8_t *data, uint8_t length) {
    if (length == 0 || length > 64) {
        return false;  /* 数据长度无效 */
    }
    
    cd_frame_t *frame = cd_list_get(&_free_head);
    if (!frame) {
        return false;  /* 无空闲帧缓冲区 */
    }
    
    /* 构造CDBUS数据帧 */
    frame->dat[0] = _cd_dev.addr;  /* 源地址 */
    frame->dat[1] = 0xFF;  /* 目标地址（广播） */
    frame->dat[2] = length;  /* 数据长度 */
    memcpy(frame->dat + 3, data, length);  /* 用户数据 */
    
    /* 计算CRC16校验（简化版MODBUS CRC） */
    uint16_t crc = 0xFFFF;
    for (int i = 0; i < length + 3; i++) {
        crc ^= frame->dat[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    frame->dat[length + 3] = crc & 0xFF;  /* CRC低字节 */
    frame->dat[length + 4] = (crc >> 8) & 0xFF;  /* CRC高字节 */
    
    /* 发送数据帧 */
    putTxFrame(&_cd_dev, frame);
    return true;
}

/**
 * @brief 读取寄存器
 * @param reg 寄存器地址（0-127）
 * @return 寄存器值
 * @details 通过SPI接口读取CDCTL01A寄存器
 * 
 * @note 读取命令：0x80 | reg地址
 */
uint8_t SimpleCDCTL::readRegister(uint8_t reg) {
    uint8_t tx_data[2] = {0x80 | reg, 0x00};  /* 读取命令 + 哑字节 */
    uint8_t rx_data[2] = {0};
    
    digitalWrite(_cs_pin, LOW);
    spiTransfer(tx_data, rx_data, 2);
    digitalWrite(_cs_pin, HIGH);
    
    return rx_data[1];  /* 返回第二个字节（实际数据） */
}

/**
 * @brief 写入寄存器
 * @param reg 寄存器地址（0-127）
 * @param value 寄存器值
 * @details 通过SPI接口写入CDCTL01A寄存器
 * 
 * @note 写入命令：reg地址 + 数据值
 */
void SimpleCDCTL::writeRegister(uint8_t reg, uint8_t value) {
    uint8_t tx_data[2] = {reg, value};
    
    digitalWrite(_cs_pin, LOW);
    spiTransfer(tx_data, NULL, 2);
    digitalWrite(_cs_pin, HIGH);
}

/**
 * @brief 检查中断状态
 * @return true有中断，false无中断
 * @details 通过INT引脚状态判断是否产生中断
 * 
 * @note INT引脚低电平有效
 */
bool SimpleCDCTL::interruptPending() {
    return digitalRead(_int_pin) == LOW;
}

/**
 * @brief 处理中断
 * @details 处理CDCTL01A芯片中断
 *          通常在ISR中调用此函数
 * 
 * @note 需要读取状态寄存器并处理相应事件
 */
void SimpleCDCTL::handleInterrupt() {
    if (_initialized) {
        /* 读取状态寄存器 */
        uint8_t status = getStatus();
        
        /* 处理接收数据中断 */
        if (status & CDSTAT_RX_PENDING) {
            /* 有接收数据 */
        }
        
        /* 处理发送完成中断 */
        if (status & CDSTAT_TX_PENDING) {
            /* 发送完成 */
        }
    }
}

/**
 * @brief SPI数据传输
 * @param tx_data 发送数据缓冲区（可为NULL）
 * @param rx_data 接收数据缓冲区（可为NULL）
 * @param length 数据长度
 * @details 执行SPI全双工数据传输
 * 
 * @note 使用8MHz SPI时钟，MSB优先，SPI模式0
 */
void SimpleCDCTL::spiTransfer(uint8_t *tx_data, uint8_t *rx_data, uint8_t length) {
    SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
    
    for (uint8_t i = 0; i < length; i++) {
        uint8_t received = SPI.transfer(tx_data ? tx_data[i] : 0x00);
        if (rx_data) {
            rx_data[i] = received;
        }
    }
    
    SPI.endTransaction();
}

/**
 * @brief 初始化帧管理
 * @details 初始化空闲帧链表
 *          为数据收发提供缓冲区管理
 */
void SimpleCDCTL::initializeFrames() {
    cd_list_init(&_free_head);  /* 初始化空闲帧链表 */
}
    
    for (int i = 0; i < FRAME_MAX; i++) {
        cd_list_put(&_free_head, &_frames[i]);
    }
}

void SimpleCDCTL::resetChip() {
    // Perform soft reset
    writeRegister(CDREG_CFG_0, 0x80); // Reset bit
    delay(1);
    writeRegister(CDREG_CFG_0, 0x00); // Clear reset
}

// Static CDBUS callbacks
cd_frame_t* SimpleCDCTL::getRxFrame(cd_dev_t *cd_dev) {
    SimpleCDCTL *self = (SimpleCDCTL*)cd_dev->priv;
    return cd_list_get(&self->_free_head);
}

void SimpleCDCTL::putTxFrame(cd_dev_t *cd_dev, cd_frame_t *frame) {
    // In this implementation, we directly send via SPI
    SimpleCDCTL *self = (SimpleCDCTL*)cd_dev->priv;
    
    // Calculate total frame length
    uint8_t total_length = frame->dat[2] + 5; // src + dst + len + data + crc
    
    // Send frame via SPI
    digitalWrite(self->_cs_pin, LOW);
    self->spiTransfer(frame->dat, NULL, total_length);
    digitalWrite(self->_cs_pin, HIGH);
    
    // Return frame to free list
    cd_list_put(&self->_free_head, frame);
}